Skip to content

Signal each class of posterior warning once - #416

Open
florence-bockting wants to merge 2 commits into
masterfrom
collect-posterior-warnings
Open

florence-bockting wants to merge 2 commits into
masterfrom
collect-posterior-warnings

Conversation

@florence-bockting

@florence-bockting florence-bockting commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Description

This PR is connected to stan-dev/posterior#470 where we introduced warning classes.
These can now be extracted and collected through the new .collect_warnings() function in loo.
This collector ensures that the same warning is printed only once (and not multiple times when e.g. running some posterior function in a loop).

This PR is related to Issue #353 (but it does not solve it entirely).

TODOs

  • test added
  • News item added

The PR was assisted by an LLM.

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.69%. Comparing base (11415f5) to head (45a7c81).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #416      +/-   ##
==========================================
+ Coverage   92.65%   92.69%   +0.04%     
==========================================
  Files          31       31              
  Lines        3240     3258      +18     
==========================================
+ Hits         3002     3020      +18     
  Misses        238      238              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

This is how benchmark results would change (along with a 95% confidence interval in relative change) if 45a7c81 is merged into master:

  • ✔️loo_function: 1.59s -> 1.59s [-0.14%, +0.32%]
  • ✔️loo_matrix: 1.58s -> 1.58s [-0.88%, +0.39%]
    Further explanation regarding interpretation and methodology can be found in the documentation.

@florence-bockting
florence-bockting marked this pull request as ready for review November 2, 2026 11:27

@jgabry jgabry left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. I made a few small suggestions.

Also one design question: before this PR users would see something like

There were 20 warnings (use warnings() to see them)"

and now with this PR they just get a single warning with no count. For something like ESS capping that's probably good, but for something like whether 1 observation or 900 observations get a Pareto k warning it could matter. Should we add something like "(n times)" to the message? I'm not sure. Maybe @avehtari and/or @VisruthSK have thoughts on this too.

Comment thread R/helpers.R
Comment on lines +289 to +302
.collect_warnings <- function(expr) {
collected <- list()
out <- withCallingHandlers(
expr,
posterior_warning = function(cnd) {
collected[[class(cnd)[1]]] <<- cnd
invokeRestart("muffleWarning")
}
)
for (cnd in collected) {
warning(cnd)
}
out
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if the function the user calls errors for whatever reason they won't see the warnings. What about this slightly modified version that moves the loop into an on.exit. I think this would allow the user to see the warnings even after an error, although I haven't tested it.

.collect_warnings <- function(expr) {
  collected <- list()
  on.exit(for (cnd in collected) warning(cnd))
  withCallingHandlers(
    expr,
    posterior_warning = function(cnd) {
      collected[[class(cnd)[1]]] <<- cnd
      invokeRestart("muffleWarning")
    }
  )
}

options(loo.cores = NULL)
})

test_that(".collect_warnings() signals each posterior class once", {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With CRAN posterior 1.7.1 there are no warning classes yet, so in CI no posterior warning ever gets to .collect_warnings() from relative_eff(), E_loo(), or loo_moment_match().

I think this test for the helper itself is good and we should keep it, but maybe add some tests that the functions users call actually signal each warning only once?

How about adding these two tests?

  1. One that calls the real loo function but with posterior mocked, e.g. local_mocked_bindings(ess_mean = ..., .package = "posterior"), with a mock that signals a classed warning, so it runs in CI now. (local_mocked_bindings() needs testthat >= 3.1.7, so we'd need to bump that in DESCRIPTION.)
  2. One that uses the real posterior functions without mocking, but we skip it until the next posterior release is available, e.g. skip_if_not(packageVersion("posterior") > "1.7.1").

Comment thread R/helpers.R
out <- withCallingHandlers(
expr,
posterior_warning = function(cnd) {
collected[[class(cnd)[1]]] <<- cnd

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this keeps the last warning of each class, not the first. The only class this really affects is pareto_tail_exceeds_draws, since those messages include actual numbers. I'm not sure that this really matters, though. What do you think?

Comment thread NEWS.md
Comment on lines +14 to +15
* Signal each class of posterior warning once per call in `relative_eff()`,
`E_loo()`, and `loo_moment_match()` by @florence-bockting in #416

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now this only applies if the user has the GitHub version of posterior. Should we mention that it's conditional on posterior > 1.7.1?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants